package org.web.standardplatform.client.builder;

import com.alibaba.druid.pool.DruidDataSource;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.web.base.helper.StringHelper;
import org.web.base.helper.excel.ExcelHelper;
import org.web.standardplatform.client.domain.attribute.AttributeDTO;
import org.web.standardplatform.client.domain.attribute.ColumnAttributeDTO;
import org.web.standardplatform.client.domain.attribute.JavaAttributeDTO;
import org.web.standardplatform.client.domain.attribute.view.TransferViewSettingDTO;
import org.web.standardplatform.client.domain.buildcode.*;
import org.web.standardplatform.client.helper.BuilderHelper;
import org.web.standardplatform.client.helper.TableSqlCheckHelper;

import java.io.*;
import java.util.*;


public class MainBuildCodeBuilder {


    public static void main(String[] args) throws Exception {

        try {
            // 前置条件，已经完成接口设计excel的配置工作。fileName中的内容已经准备就绪。
            String fileName = "D:\\test33.xlsx";
            String tableName = "user_info";
            BuildCodeRequest buildCodeRequest = new BuildCodeRequest();
            buildCodeRequest.setDbType("mysql");
            List<AttributeDTO> attributeList = getAttributeListForParseExcel(fileName, tableName);
            buildCodeRequest.setAttributeDTOList(attributeList);
            buildBuildCodeRequestListByExcel(buildCodeRequest, fileName, tableName);

            buildToStringMethod(buildCodeRequest.getCodeTableMarkDTO().getTableComment(), buildCodeRequest);
            buildBackendCode("D:\\output5", buildCodeRequest);
            // 创建后端接口服务代码已经完成，说明后端接口设计已经完成，接下来进行前端交互设计初始化。
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void buildBuildCodeRequestListByExcel(final BuildCodeRequest buildCodeRequest, String fileName, String tableName) throws Exception {
        FileInputStream fileInputStream = new FileInputStream(fileName);
        List<CodePackageDTO> codePackageDTOSList = ExcelHelper.parseExcel(fileInputStream, "main", CodePackageDTO.class, 1);
        fileInputStream = new FileInputStream(fileName);
        List<CodeTableMarkDTO> codeTableMarkDTOList = ExcelHelper.parseExcel(fileInputStream, "main", CodeTableMarkDTO.class, 1);

        for (CodePackageDTO packageDTO : codePackageDTOSList) {
            if (packageDTO.getTableName().equalsIgnoreCase(tableName)) {
                packageDTO.initPackage();
                buildCodeRequest.setCodePackageDTO(packageDTO);
                break;
            }
        }

        for (CodeTableMarkDTO codeTableMarkDTO : codeTableMarkDTOList) {
            if (codeTableMarkDTO.getTableName().equalsIgnoreCase(tableName)) {
                buildCodeRequest.setCodeTableMarkDTO(codeTableMarkDTO);
                break;
            }
        }

        buildCodeRequest.setTableName(tableName);
        buildCodeRequest.getAttributeDTOList().get(0).getColumnAttribute().setPrimaryKey(true);
        buildCodeRequest.setPrimaryAttributeDTO(buildCodeRequest.getAttributeDTOList().get(0));

        CodeClassNameDTO codeClassNameDTO = new CodeClassNameDTO(tableName);
        buildCodeRequest.setCodeClassNameDTO(codeClassNameDTO);
        CodeMethodDTO codeMethodDTO = new CodeMethodDTO(StringHelper.initUpper(buildCodeRequest.getPrimaryAttributeDTO().getJavaAttribute().getAttributeName()), buildCodeRequest.getCodeClassNameDTO().getJavaAttributeName());
        buildCodeRequest.setCodeMethodDTO(codeMethodDTO);

        CodeSpecialAttributeDTO codeSpecialAttributeDTO = new CodeSpecialAttributeDTO();
        buildCodeRequest.setCodeSpecialAttributeDTO(codeSpecialAttributeDTO);
        for (AttributeDTO attributeDTO : buildCodeRequest.getAttributeDTOList()) {
            if (SqlConverterHelper.hasStr(attributeDTO.getColumnName(), SqlConverterHelper.UPDATE_TIME)) {
                codeSpecialAttributeDTO.setUpdateAtAttribute(attributeDTO);
            } else if (SqlConverterHelper.hasStr(attributeDTO.getColumnName(), SqlConverterHelper.CREATE_TIME)) {
                codeSpecialAttributeDTO.setCreateAtAttribute(attributeDTO);
            } else if (SqlConverterHelper.hasStr(attributeDTO.getColumnName(), new String[]{"is_delete"})) {
                codeSpecialAttributeDTO.setDeleteAtAttribute(attributeDTO);
            } else if (SqlConverterHelper.hasStr(attributeDTO.getColumnName(), new String[]{"update_version"})) {
                codeSpecialAttributeDTO.setUpdateVersionAttribute(attributeDTO);
            }
            SqlConverterHelper.buildInitDefaultValue(attributeDTO.getColumnAttribute().getDetailComment(), attributeDTO.getJavaAttribute());
        }

    }


    public static void buildBackendCode(String filePath, BuildCodeRequest buildCodeRequest) throws Exception {
        String absolutePath = MainBuildCodeBuilder.class.getResource("/").getPath();
        String templatePath = absolutePath + File.separator + "template" + File.separator + "code" + File.separator + "dao";
        File baseDir = new File(filePath);
        if (!baseDir.exists()) {
            baseDir.mkdirs();
        }
        Template template = BuilderHelper.getTemplate(templatePath, "domain-template.ftl");
        File buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getDomainClassName() + ".java");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        template = BuilderHelper.getTemplate(templatePath, "dao-template.ftl");
        buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getDaoClassName() + ".java");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        template = BuilderHelper.getTemplate(templatePath, "mybatis-template.ftl");
        buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + "mapper_" + buildCodeRequest.getCodeClassNameDTO().getTableName() + ".xml");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        template = BuilderHelper.getTemplate(templatePath, "query-template.ftl");
        buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getQueryClassName() + ".java");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        templatePath = absolutePath + File.separator + "template" + File.separator + "code" + File.separator + "biz";
        template = BuilderHelper.getTemplate(templatePath, "dto-template.ftl");
        buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getDtoClassName() + ".java");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        template = BuilderHelper.getTemplate(templatePath, "biz-impl-template.ftl");
        buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getBizImplClassName() + ".java");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        template = BuilderHelper.getTemplate(templatePath, "biz-template.ftl");
        buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getBizName() + ".java");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        template = BuilderHelper.getTemplate(templatePath, "request-template.ftl");
        buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getQueryDTOClassName() + ".java");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        templatePath = absolutePath + File.separator + "template" + File.separator + "code" + File.separator + "service";
        template = BuilderHelper.getTemplate(templatePath, "service-template.ftl");
        buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getServiceClassName() + ".java");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        templatePath = absolutePath + File.separator + "template" + File.separator + "code" + File.separator + "controller";
        template = BuilderHelper.getTemplate(templatePath, "controller-template.ftl");
        buildCodeFile = new File(baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getControllerClassName() + ".java");
        process(template, new FileWriter(buildCodeFile), buildCodeRequest);

        String excelFile = baseDir.getAbsolutePath() + File.separator + buildCodeRequest.getCodeClassNameDTO().getDtoClassName() + ".xlsx";
        buildDownloadModelExcel(excelFile, buildCodeRequest);
    }

    public static void buildDownloadModelExcel(String fileName, BuildCodeRequest buildCodeRequest) throws IOException {

        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet(buildCodeRequest.getCodeClassNameDTO().getDtoClassName());
        List<String> shortNameList = new ArrayList<>();
        List<String> attributeList = new ArrayList<>();
        shortNameList.add("中文");
        attributeList.add("英文");
        for (AttributeDTO attributeDTO : buildCodeRequest.getAttributeDTOList()) {
            shortNameList.add(attributeDTO.getColumnAttribute().getShortName());
            attributeList.add(attributeDTO.getJavaAttribute().getAttributeName());
        }
        ExcelHelper.writeRow(sheet, 0, shortNameList);
        ExcelHelper.writeRow(sheet, 1, attributeList);

        File file = new File(fileName);
        if (file.exists()) {
            file.delete();
        }

        FileOutputStream outputStream = new FileOutputStream(fileName);
        workbook.write(outputStream);
        outputStream.flush();
        outputStream.close();
    }


    public static void process(Template template, Writer out, BuildCodeRequest buildCodeRequest) throws IOException, TemplateException {
        Map map = new HashMap();
        map.put("buildCodeRequest", buildCodeRequest);
        template.process(map, out);
    }

    public static String generateJavaPatternName(String value) {

        DruidDataSource ds = new DruidDataSource();

        String[] array = value.split("_");
        String attrsName = "";
        if (array.length == 1) {
            if (array[0].toUpperCase().equals(array[0])) {
                return array[0].toLowerCase();
            }
            return StringHelper.initLower(array[0]);
        } else {
            for (int i = 0; i < array.length; i++) {
                if (i == 0) {
                    attrsName = attrsName + array[i].toLowerCase();
                } else {
                    attrsName = attrsName + StringHelper.initUpper(array[i].toLowerCase());
                }
            }
        }
        return attrsName;
    }

    public static void buildToStringMethod(String tableComment, BuildCodeRequest buildCodeRequest) {
        List<String> uniqueColumnList = TableSqlCheckHelper.parseColumnListByTableComment("逻辑唯一属性信息", tableComment);

        if (uniqueColumnList != null && !uniqueColumnList.isEmpty()) {
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.append("// 重写 toString() 方法，").append(uniqueColumnList).append(" 组合逻辑唯一。\n");
            stringBuilder.append("    @Override\n");
            stringBuilder.append("    public String toString() {\n");
            stringBuilder.append("        return \"")
                    .append(buildCodeRequest.getCodeClassNameDTO().getDomainClassName())
                    .append("{\" +\n");

            // 使用 StringJoiner 构建字段输出部分
            StringJoiner joiner = new StringJoiner(" + \", \" +\n            ");
            for (String columnName : uniqueColumnList) {
                String attributeName = generateJavaPatternName(columnName.trim());
                joiner.add("\"" + attributeName + "=\" + " + attributeName);
            }

            stringBuilder.append("            ")
                    .append(joiner.toString())
                    .append(" + \"}\";\n");

            stringBuilder.append("    }\n\n");

            buildCodeRequest.setToStringMethod(stringBuilder.toString());
        }
    }


    public static List<AttributeDTO> getAttributeListForParseExcel(String fileName, String sheetName) throws Exception {
        FileInputStream inputStream = new FileInputStream(fileName);
        List<AttributeDTO> attributeList = ExcelHelper.parseExcel(inputStream, sheetName, AttributeDTO.class, 3);
        inputStream.close();
        inputStream = new FileInputStream(fileName);

        List<ColumnAttributeDTO> columnAttributeList = ExcelHelper.parseExcel(inputStream, sheetName, ColumnAttributeDTO.class, 3);
        Map<String, ColumnAttributeDTO> columnAttributeMap = columnAttributeList.stream().collect(java.util.stream.Collectors.toMap(ColumnAttributeDTO::getColumnName, columnAttribute -> columnAttribute));
        inputStream.close();
        inputStream = new FileInputStream(fileName);

        List<JavaAttributeDTO> javaAttributeList = ExcelHelper.parseExcel(inputStream, sheetName, JavaAttributeDTO.class, 3);
        Map<String, JavaAttributeDTO> javaAttributeMap = javaAttributeList.stream().collect(java.util.stream.Collectors.toMap(JavaAttributeDTO::getColumnName, columnAttribute -> columnAttribute));
        inputStream.close();
        inputStream = new FileInputStream(fileName);

        List<TransferViewSettingDTO> transferViewSettingDTO = ExcelHelper.parseExcel(inputStream, sheetName, TransferViewSettingDTO.class, 3);
        Map<String, TransferViewSettingDTO> viewAttributeMap = transferViewSettingDTO.stream().collect(java.util.stream.Collectors.toMap(TransferViewSettingDTO::getColumnName, columnAttribute -> columnAttribute));
        inputStream.close();

        for (AttributeDTO attribute : attributeList) {
            attribute.setColumnAttribute(columnAttributeMap.get(attribute.getColumnName()));
            attribute.setJavaAttribute(javaAttributeMap.get(attribute.getColumnName()));
            attribute.setTransferViewSettingDTO(viewAttributeMap.get(attribute.getColumnName()));
        }

        return attributeList;
    }
}
